/*
 Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.

 Redistributions in binary form must reproduce the above
 copyright notice, this list of conditions and the following
 disclaimer in the documentation and/or other materials provided
 with the distribution.

 Neither the name of 3Dlabs Inc. Ltd. nor the names of its
 contributors may be used to endorse or promote products derived
 from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */

/****************************************************************************\
Copyright (c) 2002, NVIDIA Corporation.

 NVIDIA Corporation("NVIDIA") supplies this software to you in
 consideration of your agreement to the following terms, and your use,
 installation, modification or redistribution of this NVIDIA software
 constitutes acceptance of these terms.  If you do not agree with these
 terms, please do not use, install, modify or redistribute this NVIDIA
 software.

 In consideration of your agreement to abide by the following terms, and
 subject to these terms, NVIDIA grants you a personal, non-exclusive
 license, under NVIDIA's copyrights in this original NVIDIA software (the
 "NVIDIA Software"), to use, reproduce, modify and redistribute the
 NVIDIA Software, with or without modifications, in source and/or binary
 forms; provided that if you redistribute the NVIDIA Software, you must
 retain the copyright notice of NVIDIA, this notice and the following
 text and disclaimers in all such redistributions of the NVIDIA Software.
 Neither the name, trademarks, service marks nor logos of NVIDIA
 Corporation may be used to endorse or promote products derived from the
 NVIDIA Software without specific prior written permission from NVIDIA.
 Except as expressly stated in this notice, no other rights or licenses
 express or implied, are granted by NVIDIA herein, including but not
 limited to any patent rights that may be infringed by your derivative
 works or by other works in which the NVIDIA Software may be
 incorporated. No hardware is licensed hereunder.

 THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
 WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
 NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
 PRODUCTS.

 IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
 INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
 OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
 NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
 TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
 NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 \****************************************************************************/
/*
 * cpp.c
 */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "slglobals.h"

static int CPPif(yystypepp * yylvalpp);

/* Don't use memory.c's replacements, as we clean up properly here */
#undef malloc
#undef free

extern void setVersionNumber(int);

static int bindAtom = 0;
static int constAtom = 0;
static int defaultAtom = 0;
static int defineAtom = 0;
static int definedAtom = 0;
static int elseAtom = 0;
static int elifAtom = 0;
static int endifAtom = 0;
static int ifAtom = 0;
static int ifdefAtom = 0;
static int ifndefAtom = 0;
static int includeAtom = 0;
static int lineAtom = 0;
static int pragmaAtom = 0;
static int texunitAtom = 0;
static int undefAtom = 0;
static int errorAtom = 0;
static int __LINE__Atom = 0;
static int __FILE__Atom = 0;
static int __VERSION__Atom = 0;
static int versionAtom = 0;
static int extensionAtom = 0;

static Scope *macros = 0;
#define MAX_MACRO_ARGS  64
#define MAX_IF_NESTING  64

static SourceLoc ifloc; /* outermost #if */

int InitCPP(void)
{
	char buffer[64], *t;
	const char *f;
	/* Add various atoms needed by the CPP line scanner: */
	bindAtom = LookUpAddString(atable, "bind");
	constAtom = LookUpAddString(atable, "const");
	defaultAtom = LookUpAddString(atable, "default");
	defineAtom = LookUpAddString(atable, "define");
	definedAtom = LookUpAddString(atable, "defined");
	elifAtom = LookUpAddString(atable, "elif");
	elseAtom = LookUpAddString(atable, "else");
	endifAtom = LookUpAddString(atable, "endif");
	ifAtom = LookUpAddString(atable, "if");
	ifdefAtom = LookUpAddString(atable, "ifdef");
	ifndefAtom = LookUpAddString(atable, "ifndef");
	includeAtom = LookUpAddString(atable, "include");
	lineAtom = LookUpAddString(atable, "line");
	pragmaAtom = LookUpAddString(atable, "pragma");
	texunitAtom = LookUpAddString(atable, "texunit");
	undefAtom = LookUpAddString(atable, "undef");
	errorAtom = LookUpAddString(atable, "error");
	__LINE__Atom = LookUpAddString(atable, "__LINE__");
	__FILE__Atom = LookUpAddString(atable, "__FILE__");
	__VERSION__Atom = LookUpAddString(atable, "__VERSION__");
	versionAtom = LookUpAddString(atable, "version");
	extensionAtom = LookUpAddString(atable, "extension");
	macros = NewScopeInPool(mem_CreatePool(0, 0));
	strcpy(buffer, "PROFILE_");
	t = buffer + strlen(buffer);
	f = cpp->options.profileString;
	while ((isalnum(*f) || *f == '_') && t < buffer + sizeof(buffer) - 1)
		*t++ = toupper(*f++);
	*t = 0;
	return 1;
} /* InitCPP */

int FreeCPP(void)
{
	if (macros) {
		mem_FreePool(macros->pool);
		macros = 0;
	}

	return 1;
}

int FinalCPP(void)
{
	if (cpp->ifdepth)
		CPPErrorToInfoLog("#if mismatch");
	return 1;
}

static int CPPdefine(yystypepp * yylvalpp)
{
	int token, name, args[MAX_MACRO_ARGS], argc;
	const char *message;
	MacroSymbol mac;
	Symbol *symb;
	SourceLoc dummyLoc;
	memset(&mac, 0, sizeof(mac));
	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	if (token != CPP_IDENTIFIER) {
		CPPErrorToInfoLog("#define");
		return token;
	}
	name = yylvalpp->sc_ident;
	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	if (token == '(' && !yylvalpp->sc_int) {
		/* gather arguments */
		argc = 0;
		do {
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			if (argc == 0 && token == ')')
				break;
			if (token != CPP_IDENTIFIER) {
				CPPErrorToInfoLog("#define");
				return token;
			}
			if (argc < MAX_MACRO_ARGS)
				args[argc++] = yylvalpp->sc_ident;
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		} while (token == ',');
		if (token != ')') {
			CPPErrorToInfoLog("#define");
			return token;
		}
		mac.argc = argc;
		mac.args = (int *) mem_Alloc(macros->pool, argc * sizeof(int));
		memcpy(mac.args, args, argc * sizeof(int));
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	}
	mac.body = NewTokenStream(GetAtomString(atable, name), macros->pool);
	while (token != '\n') {
		while (token == '\\') {
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			if (token == '\n')
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			else
				RecordToken(mac.body, '\\', yylvalpp);
		}
		RecordToken(mac.body, token, yylvalpp);
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	};

	symb = LookUpSymbol(macros, name);
	if (symb) {
		if (!symb->details.mac.undef) {
			/* already defined -- need to make sure they are identical */
			if (symb->details.mac.argc != mac.argc)
				goto error;
			for (argc = 0; argc < mac.argc; argc++)
				if (symb->details.mac.args[argc] != mac.args[argc])
					goto error;
			RewindTokenStream(symb->details.mac.body);
			RewindTokenStream(mac.body);
			do {
				int old_lval, old_token;
				old_token = ReadToken(symb->details.mac.body, yylvalpp);
				old_lval = yylvalpp->sc_int;
				token = ReadToken(mac.body, yylvalpp);
				if (token != old_token || yylvalpp->sc_int != old_lval) {
					error: StoreStr("Macro Redefined");
					StoreStr(GetStringOfAtom(atable, name));
					message = GetStrfromTStr();
					DecLineNumber();
					CPPShInfoLogMsg(message);
					IncLineNumber();
					ResetTString();
					break;
				}
			} while (token > 0);
		}
		/* FreeMacro(&symb->details.mac); */
	} else {
		dummyLoc.file = 0;
		dummyLoc.line = 0;
		symb = AddSymbol(&dummyLoc, macros, name, MACRO_S);
	}
	symb->details.mac = mac;
	return '\n';
} /* CPPdefine */

static int CPPundef(yystypepp * yylvalpp)
{
	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	Symbol *symb;
	if (token == '\n') {
		CPPErrorToInfoLog("#undef");
		return token;
	}
	if (token != CPP_IDENTIFIER)
		goto error;
	symb = LookUpSymbol(macros, yylvalpp->sc_ident);
	if (symb) {
		symb->details.mac.undef = 1;
	}
	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	if (token != '\n') {
		error: CPPErrorToInfoLog("#undef");
	}
	return token;
} /* CPPundef */

/* CPPelse -- skip forward to appropriate spot.  This is actually used
 ** to skip to and #endif after seeing an #else, AND to skip to a #else,
 ** #elif, or #endif after a #if/#ifdef/#ifndef/#elif test was false
 */

static int CPPelse(int matchelse, yystypepp * yylvalpp)
{
	int atom, depth = 0;
	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);

	while (token > 0) {
		if (token != '#') {
			while (token != '\n')
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);

			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			continue;
		}
		if ((token = cpp->currentInput->scan(cpp->currentInput, yylvalpp))
				!= CPP_IDENTIFIER)
			continue;
		atom = yylvalpp->sc_ident;
		if (atom == ifAtom || atom == ifdefAtom || atom == ifndefAtom) {
			depth++;
			cpp->ifdepth++;
			cpp->elsetracker++;
			cpp->elsedepth[cpp->elsetracker] = 0;
		} else if (atom == endifAtom) {
			if (--depth < 0) {
				cpp->elsedepth[cpp->elsetracker] = 0;
				--cpp->elsetracker;
				if (cpp->ifdepth)
					--cpp->ifdepth;
				break;
			} else {
				cpp->elsedepth[cpp->elsetracker] = 0;
				--cpp->elsetracker;
				--cpp->ifdepth;
			}
		} else if (((int) (matchelse) != 0) && depth == 0) {
			if (atom == elseAtom) {
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
				if (token != '\n') {
					CPPWarningToInfoLog(
							"unexpected tokens following #else preprocessor directive - expected a newline");
					while (token != '\n')
						token = cpp->currentInput->scan(cpp->currentInput,
								yylvalpp);
				}
				break;
			} else if (atom == elifAtom) {
				/* we decrement cpp->ifdepth here, because CPPif will increment
				 * it and we really want to leave it alone */
				if (cpp->ifdepth) {
					--cpp->ifdepth;
					--cpp->elsetracker;
				}
				return CPPif(yylvalpp);
			}
		} else if ((atom == elseAtom) && (!ChkCorrectElseNesting())) {
			CPPErrorToInfoLog("#else after a #else in CPPelse");
			cpp->CompileError = 1;
		}
	}

	return token;
}

enum eval_prec {
	MIN_PREC,
	COND,
	LOGOR,
	LOGAND,
	OR,
	XOR,
	AND,
	EQUAL,
	RELATION,
	SHIFT,
	ADD,
	MUL,
	UNARY,
	MAX_PREC
};

static int op_logor(int a, int b)
{
	return a || b;
}
static int op_logand(int a, int b)
{
	return a && b;
}
static int op_or(int a, int b)
{
	return a | b;
}
static int op_xor(int a, int b)
{
	return a ^ b;
}
static int op_and(int a, int b)
{
	return a & b;
}
static int op_eq(int a, int b)
{
	return a == b;
}
static int op_ne(int a, int b)
{
	return a != b;
}
static int op_ge(int a, int b)
{
	return a >= b;
}
static int op_le(int a, int b)
{
	return a <= b;
}
static int op_gt(int a, int b)
{
	return a > b;
}
static int op_lt(int a, int b)
{
	return a < b;
}
static int op_shl(int a, int b)
{
	return a << b;
}
static int op_shr(int a, int b)
{
	return a >> b;
}
static int op_add(int a, int b)
{
	return a + b;
}
static int op_sub(int a, int b)
{
	return a - b;
}
static int op_mul(int a, int b)
{
	return a * b;
}
static int op_div(int a, int b)
{
	return a / b;
}
static int op_mod(int a, int b)
{
	return a % b;
}
static int op_pos(int a)
{
	return a;
}
static int op_neg(int a)
{
	return -a;
}
static int op_cmpl(int a)
{
	return ~a;
}
static int op_not(int a)
{
	return !a;
}

struct {
	int token, prec, (*op)(int, int);
} binop[] = { { CPP_OR_OP, LOGOR, op_logor }, { CPP_AND_OP, LOGAND, op_logand },
		{ '|', OR, op_or }, { '^', XOR, op_xor }, { '&', AND, op_and }, {
				CPP_EQ_OP, EQUAL, op_eq }, { CPP_NE_OP, EQUAL, op_ne }, { '>',
				RELATION, op_gt }, { CPP_GE_OP, RELATION, op_ge }, { '<',
				RELATION, op_lt }, { CPP_LE_OP, RELATION, op_le }, {
				CPP_LEFT_OP, SHIFT, op_shl }, { CPP_RIGHT_OP, SHIFT, op_shr }, {
				'+', ADD, op_add }, { '-', ADD, op_sub }, { '*', MUL, op_mul },
		{ '/', MUL, op_div }, { '%', MUL, op_mod }, };

struct {
	int token, (*op)(int);
} unop[] =
		{ { '+', op_pos }, { '-', op_neg }, { '~', op_cmpl }, { '!', op_not }, };

#define ALEN(A) (sizeof(A)/sizeof(A[0]))

static int eval(int token, int prec, int *res, int *err, yystypepp * yylvalpp)
{
	int i, val;
	Symbol *s;
	if (token == CPP_IDENTIFIER) {
		if (yylvalpp->sc_ident == definedAtom) {
			int needclose = 0;
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			if (token == '(') {
				needclose = 1;
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			}
			if (token != CPP_IDENTIFIER)
				goto error;
			*res = (s = LookUpSymbol(macros, yylvalpp->sc_ident)) ?
					!s->details.mac.undef : 0;
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			if (needclose) {
				if (token != ')')
					goto error;
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			}
		} else if (MacroExpand(yylvalpp->sc_ident, yylvalpp)) {
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			return eval(token, prec, res, err, yylvalpp);
		} else {
			goto error;
		}
	} else if (token == CPP_INTCONSTANT) {
		*res = yylvalpp->sc_int;
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	} else if (token == '(') {
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		token = eval(token, MIN_PREC, res, err, yylvalpp);
		if (!*err) {
			if (token != ')')
				goto error;
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		}
	} else {
		for (i = ALEN(unop) - 1; i >= 0; i--) {
			if (unop[i].token == token)
				break;
		}
		if (i >= 0) {
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			token = eval(token, UNARY, res, err, yylvalpp);
			*res = unop[i].op(*res);
		} else {
			goto error;
		}
	}
	while (!*err) {
		if (token == ')' || token == '\n')
			break;
		for (i = ALEN(binop) - 1; i >= 0; i--) {
			if (binop[i].token == token)
				break;
		}
		if (i < 0 || binop[i].prec <= prec)
			break;
		val = *res;
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		token = eval(token, binop[i].prec, res, err, yylvalpp);
		*res = binop[i].op(val, *res);
	}
	return token;
	error: CPPErrorToInfoLog("incorrect preprocessor directive");
	*err = 1;
	*res = 0;
	return token;
} /* eval */

static int CPPif(yystypepp * yylvalpp)
{
	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	int res = 0, err = 0;
	cpp->elsetracker++;
	cpp->elsedepth[cpp->elsetracker] = 0;
	if (!cpp->ifdepth++)
		ifloc = *cpp->tokenLoc;
	if (cpp->ifdepth > MAX_IF_NESTING) {
		CPPErrorToInfoLog("max #if nesting depth exceeded");
		return 0;
	}
	token = eval(token, MIN_PREC, &res, &err, yylvalpp);
	if (token != '\n') {
		CPPWarningToInfoLog(
				"unexpected tokens following the preprocessor directive - expected a newline");
		while (token != '\n')
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	}
	if (!res && !err) {
		token = CPPelse(1, yylvalpp);
	}

	return token;
} /* CPPif */

static int CPPifdef(int defined, yystypepp * yylvalpp)
{
	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	int name = yylvalpp->sc_ident;
	if (++cpp->ifdepth > MAX_IF_NESTING) {
		CPPErrorToInfoLog("max #if nesting depth exceeded");
		return 0;
	}
	cpp->elsetracker++;
	cpp->elsedepth[cpp->elsetracker] = 0;
	if (token != CPP_IDENTIFIER) {
		defined ? CPPErrorToInfoLog("ifdef") : CPPErrorToInfoLog("ifndef");
	} else {
		Symbol *s = LookUpSymbol(macros, name);
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		if (token != '\n') {
			CPPWarningToInfoLog(
					"unexpected tokens following #ifdef preprocessor directive - expected a newline");
			while (token != '\n')
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		}
		if (((s && !s->details.mac.undef) ? 1 : 0) != defined) {
			token = CPPelse(1, yylvalpp);
		}
	}

	return token;
} /* CPPifdef */

static int CPPline(yystypepp * yylvalpp)
{
	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	if (token == '\n') {
		DecLineNumber();
		CPPErrorToInfoLog("#line");
		IncLineNumber();
		return token;
	} else if (token == CPP_INTCONSTANT) {
		yylvalpp->sc_int = atoi(yylvalpp->symbol_name);
		/*SetLineNumber(yylvalpp->sc_int); */
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);

		if (token == CPP_INTCONSTANT) {
			yylvalpp->sc_int = atoi(yylvalpp->symbol_name);
			/*SetStringNumber(yylvalpp->sc_int); */
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			if (token != '\n')
				CPPErrorToInfoLog("#line");
		} else if (token == '\n') {
			return token;
		} else {
			CPPErrorToInfoLog("#line");
		}
	} else {
		CPPErrorToInfoLog("#line");
	}
	return token;
}

static int CPPerror(yystypepp * yylvalpp)
{

	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	const char *message;

	while (token != '\n') {
		if (token == CPP_FLOATCONSTANT || token == CPP_INTCONSTANT) {
			StoreStr(yylvalpp->symbol_name);
		} else if (token == CPP_IDENTIFIER || token == CPP_STRCONSTANT) {
			StoreStr(GetStringOfAtom(atable, yylvalpp->sc_ident));
		} else {
			StoreStr(GetStringOfAtom(atable, token));
		}
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	}
	DecLineNumber();
	/*store this msg into the shader's information log..set the Compile Error flag!!!! */
	message = GetStrfromTStr();
	CPPShInfoLogMsg(message);
	ResetTString();
	cpp->CompileError = 1;
	IncLineNumber();
	return '\n';
} /*CPPerror */

static int CPPpragma(yystypepp * yylvalpp)
{
	char SrcStrName[2];
	char** allTokens;
	int tokenCount = 0;
	int maxTokenCount = 10;
	const char* SrcStr;
	int i;

	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);

	if (token == '\n') {
		DecLineNumber();
		CPPErrorToInfoLog("#pragma");
		IncLineNumber();
		return token;
	}

	allTokens = (char**) malloc(sizeof(char*) * maxTokenCount);

	while (token != '\n') {
		if (tokenCount >= maxTokenCount) {
			maxTokenCount *= 2;
			allTokens = (char**) realloc((char**) allTokens,
					sizeof(char*) * maxTokenCount);
		}
		switch (token) {
		case CPP_IDENTIFIER:
			SrcStr = GetAtomString(atable, yylvalpp->sc_ident);
			allTokens[tokenCount] = (char*) malloc(strlen(SrcStr) + 1);
			strcpy(allTokens[tokenCount++], SrcStr);
			break;
		case CPP_INTCONSTANT:
			SrcStr = yylvalpp->symbol_name;
			allTokens[tokenCount] = (char*) malloc(strlen(SrcStr) + 1);
			strcpy(allTokens[tokenCount++], SrcStr);
			break;
		case CPP_FLOATCONSTANT:
			SrcStr = yylvalpp->symbol_name;
			allTokens[tokenCount] = (char*) malloc(strlen(SrcStr) + 1);
			strcpy(allTokens[tokenCount++], SrcStr);
			break;
		case -1:
			/* EOF */
			CPPShInfoLogMsg("#pragma directive must end with a newline");
			return token;
		default:
			SrcStrName[0] = token;
			SrcStrName[1] = '\0';
			allTokens[tokenCount] = (char*) malloc(2);
			strcpy(allTokens[tokenCount++], SrcStrName);
		}
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	}

	cpp->currentInput->ungetch(cpp->currentInput, token, yylvalpp);
	HandlePragma((const char**) allTokens, tokenCount);
	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);

	for (i = 0; i < tokenCount; ++i) {
		free(allTokens[i]);
	}
	free(allTokens);

	return token;
} /* CPPpragma */

#define GLSL_100_VERSION_NUMBER 110
#define GLSL_110_VERSION_NUMBER 110
#define GLSL_120_VERSION_NUMBER 120

static int CPPversion(yystypepp * yylvalpp)
{

	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);

	if (cpp->notAVersionToken == 1)
		CPPShInfoLogMsg(
				"#version must occur before any other statement in the program");

	if (token == '\n') {
		DecLineNumber();
		CPPErrorToInfoLog("#version");
		IncLineNumber();
		return token;
	}
	if (token != CPP_INTCONSTANT)
		CPPErrorToInfoLog("#version");

	yylvalpp->sc_int = atoi(yylvalpp->symbol_name);
	setVersionNumber(yylvalpp->sc_int);

	if (yylvalpp->sc_int != GLSL_100_VERSION_NUMBER
			&& yylvalpp->sc_int != GLSL_110_VERSION_NUMBER
			&& yylvalpp->sc_int != GLSL_120_VERSION_NUMBER) {
		CPPShInfoLogMsg("Version number not supported by GL2");
	}

	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);

	if (token == '\n') {
		return token;
	} else {
		CPPErrorToInfoLog("#version");
	}
	return token;
} /* CPPversion */

static int CPPextension(yystypepp * yylvalpp)
{

	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	char extensionName[80];

	if (token == '\n') {
		DecLineNumber();
		CPPShInfoLogMsg("extension name not specified");
		IncLineNumber();
		return token;
	}

	if (token != CPP_IDENTIFIER)
		CPPErrorToInfoLog("#extension");

	strcpy(extensionName, GetAtomString(atable, yylvalpp->sc_ident));

	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	if (token != ':') {
		CPPShInfoLogMsg("':' missing after extension name");
		return token;
	}

	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	if (token != CPP_IDENTIFIER) {
		CPPShInfoLogMsg("behavior for extension not specified");
		return token;
	}

	updateExtensionBehavior(extensionName,
			GetAtomString(atable, yylvalpp->sc_ident));

	token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	if (token == '\n') {
		return token;
	} else {
		CPPErrorToInfoLog("#extension");
	}
	return token;
} /* CPPextension */

int readCPPline(yystypepp * yylvalpp)
{
	int token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	const char *message;
	int isVersion = 0;

	if (token == CPP_IDENTIFIER) {
		if (yylvalpp->sc_ident == defineAtom) {
			token = CPPdefine(yylvalpp);
		} else if (yylvalpp->sc_ident == elseAtom) {
			if (ChkCorrectElseNesting()) {
				if (!cpp->ifdepth) {
					CPPErrorToInfoLog("#else mismatch");
					cpp->CompileError = 1;
				}
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
				if (token != '\n') {
					CPPWarningToInfoLog(
							"unexpected tokens following #else preprocessor directive - expected a newline");
					while (token != '\n')
						token = cpp->currentInput->scan(cpp->currentInput,
								yylvalpp);
				}
				token = CPPelse(0, yylvalpp);
			} else {
				CPPErrorToInfoLog("#else after a #else");
				cpp->ifdepth = 0;
				cpp->notAVersionToken = 1;
				return 0;
			}
		} else if (yylvalpp->sc_ident == elifAtom) {
			if (!cpp->ifdepth) {
				CPPErrorToInfoLog("#elif mismatch");
				cpp->CompileError = 1;
			}
			/* this token is really a dont care, but we still need to eat the tokens */
			token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			while (token != '\n')
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
			token = CPPelse(0, yylvalpp);
		} else if (yylvalpp->sc_ident == endifAtom) {
			cpp->elsedepth[cpp->elsetracker] = 0;
			--cpp->elsetracker;
			if (!cpp->ifdepth) {
				CPPErrorToInfoLog("#endif mismatch");
				cpp->CompileError = 1;
			} else
				--cpp->ifdepth;
		} else if (yylvalpp->sc_ident == ifAtom) {
			token = CPPif(yylvalpp);
		} else if (yylvalpp->sc_ident == ifdefAtom) {
			token = CPPifdef(1, yylvalpp);
		} else if (yylvalpp->sc_ident == ifndefAtom) {
			token = CPPifdef(0, yylvalpp);
		} else if (yylvalpp->sc_ident == lineAtom) {
			token = CPPline(yylvalpp);
		} else if (yylvalpp->sc_ident == pragmaAtom) {
			token = CPPpragma(yylvalpp);
		} else if (yylvalpp->sc_ident == undefAtom) {
			token = CPPundef(yylvalpp);
		} else if (yylvalpp->sc_ident == errorAtom) {
			token = CPPerror(yylvalpp);
		} else if (yylvalpp->sc_ident == versionAtom) {
			token = CPPversion(yylvalpp);
			isVersion = 1;
		} else if (yylvalpp->sc_ident == extensionAtom) {
			token = CPPextension(yylvalpp);
		} else {
			StoreStr("Invalid Directive");
			StoreStr(GetStringOfAtom(atable, yylvalpp->sc_ident));
			message = GetStrfromTStr();
			CPPShInfoLogMsg(message);
			ResetTString();
		}
	}
	while (token != '\n' && token != 0 && token != EOF) {
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
	}

	cpp->notAVersionToken = !isVersion;

	return token;
} /* readCPPline */

void FreeMacro(MacroSymbol *s)
{
	DeleteTokenStream(s->body);
}

static int eof_scan(InputSrc *in, yystypepp * yylvalpp)
{
	return -1;
}
static void noop(InputSrc *in, int ch, yystypepp * yylvalpp)
{
}

static void PushEofSrc()
{
	InputSrc *in = (InputSrc *) malloc(sizeof(InputSrc));
	memset(in, 0, sizeof(InputSrc));
	in->scan = eof_scan;
	in->getch = eof_scan;
	in->ungetch = noop;
	in->prev = cpp->currentInput;
	cpp->currentInput = in;
}

static void PopEofSrc()
{
	if (cpp->currentInput->scan == eof_scan) {
		InputSrc *in = cpp->currentInput;
		cpp->currentInput = in->prev;
		free(in);
	}
}

static TokenStream *PrescanMacroArg(TokenStream *a, yystypepp * yylvalpp)
{
	int token;
	TokenStream *n;
	RewindTokenStream(a);
	do {
		token = ReadToken(a, yylvalpp);
		if (token == CPP_IDENTIFIER && LookUpSymbol(macros, yylvalpp->sc_ident))
			break;
	} while (token > 0);
	if (token <= 0)
		return a;
	n = NewTokenStream("macro arg", 0);
	PushEofSrc();
	ReadFromTokenStream(a, 0, 0);
	while ((token = cpp->currentInput->scan(cpp->currentInput, yylvalpp)) > 0) {
		if (token == CPP_IDENTIFIER
				&& MacroExpand(yylvalpp->sc_ident, yylvalpp))
			continue;
		RecordToken(n, token, yylvalpp);
	}
	PopEofSrc();
	DeleteTokenStream(a);
	return n;
} /* PrescanMacroArg */

typedef struct MacroInputSrc {
	InputSrc base;
	MacroSymbol *mac;
	TokenStream **args;
} MacroInputSrc;

/* macro_scan ---
 ** return the next token for a macro expanion, handling macro args
 */
static int macro_scan(MacroInputSrc *in, yystypepp * yylvalpp)
{
	int i;
	int token = ReadToken(in->mac->body, yylvalpp);
	if (token == CPP_IDENTIFIER) {
		for (i = in->mac->argc - 1; i >= 0; i--)
			if (in->mac->args[i] == yylvalpp->sc_ident)
				break;
		if (i >= 0) {
			ReadFromTokenStream(in->args[i], yylvalpp->sc_ident, 0);
			return cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		}
	}
	if (token > 0)
		return token;
	in->mac->busy = 0;
	cpp->currentInput = in->base.prev;
	if (in->args) {
		for (i = in->mac->argc - 1; i >= 0; i--)
			DeleteTokenStream(in->args[i]);
		free(in->args);
	}
	free(in);
	return cpp->currentInput->scan(cpp->currentInput, yylvalpp);
} /* macro_scan */

/* MacroExpand
 ** check an identifier (atom) to see if it a macro that should be expanded.
 ** If it is, push an InputSrc that will produce the appropriate expansion
 ** and return TRUE.  If not, return FALSE.
 */

int MacroExpand(int atom, yystypepp * yylvalpp)
{
	Symbol *sym = LookUpSymbol(macros, atom);
	MacroInputSrc *in;
	int i, j, token, depth = 0;
	const char *message;
	if (atom == __LINE__Atom) {
		yylvalpp->sc_int = GetLineNumber();
		sprintf(yylvalpp->symbol_name, "%d", yylvalpp->sc_int);
		UngetToken(CPP_INTCONSTANT, yylvalpp);
		return 1;
	}
	if (atom == __FILE__Atom) {
		yylvalpp->sc_int = GetStringNumber();
		sprintf(yylvalpp->symbol_name, "%d", yylvalpp->sc_int);
		UngetToken(CPP_INTCONSTANT, yylvalpp);
		return 1;
	}
	if (atom == __VERSION__Atom) {
		strcpy(yylvalpp->symbol_name, "100");
		yylvalpp->sc_int = atoi(yylvalpp->symbol_name);
		UngetToken(CPP_INTCONSTANT, yylvalpp);
		return 1;
	}
	if (!sym || sym->details.mac.undef)
		return 0;
	if (sym->details.mac.busy)
		return 0; /* no recursive expansions */
	in = (MacroInputSrc *) malloc(sizeof(*in));
	memset(in, 0, sizeof(*in));
#ifdef _WIN32
	in->base.scan = (int (__cdecl*)(InputSrc *, yystypepp *))macro_scan;
#else /* _WIN32 */
	in->base.scan = (void *) macro_scan;
#endif /* _WIN32 */
	in->base.line = cpp->currentInput->line;
	in->base.name = cpp->currentInput->name;
	in->mac = &sym->details.mac;
	if (sym->details.mac.args) {
		token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
		if (token != '(') {
			UngetToken(token, yylvalpp);
			yylvalpp->sc_ident = atom;
			return 0;
		}
		in->args = (TokenStream **) malloc(
				in->mac->argc * sizeof(TokenStream *));
		for (i = 0; i < in->mac->argc; i++)
			in->args[i] = NewTokenStream("macro arg", 0);
		i = 0;
		j = 0;
		do {
			depth = 0;
			while (1) {
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
				if (token <= 0) {
					StoreStr("EOF in Macro ");
					StoreStr(GetStringOfAtom(atable, atom));
					message = GetStrfromTStr();
					CPPShInfoLogMsg(message);
					ResetTString();
					return 1;
				}
				if ((in->mac->argc == 0) && (token != ')'))
					break;
				if (depth == 0 && (token == ',' || token == ')'))
					break;
				if (token == '(')
					depth++;
				if (token == ')')
					depth--;
				RecordToken(in->args[i], token, yylvalpp);
				j = 1;
			}
			if (token == ')') {
				if ((in->mac->argc == 1) && j == 0)
					break;
				i++;
				break;
			}
			i++;
		} while (i < in->mac->argc);

		if (i < in->mac->argc) {
			StoreStr("Too few args in Macro ");
			StoreStr(GetStringOfAtom(atable, atom));
			message = GetStrfromTStr();
			CPPShInfoLogMsg(message);
			ResetTString();
		} else if (token != ')') {
			depth = 0;
			while (token >= 0 && (depth > 0 || token != ')')) {
				if (token == ')')
					depth--;
				token = cpp->currentInput->scan(cpp->currentInput, yylvalpp);
				if (token == '(')
					depth++;
			}

			if (token <= 0) {
				StoreStr("EOF in Macro ");
				StoreStr(GetStringOfAtom(atable, atom));
				message = GetStrfromTStr();
				CPPShInfoLogMsg(message);
				ResetTString();
				return 1;
			}
			StoreStr("Too many args in Macro ");
			StoreStr(GetStringOfAtom(atable, atom));
			message = GetStrfromTStr();
			CPPShInfoLogMsg(message);
			ResetTString();
		}
		for (i = 0; i < in->mac->argc; i++) {
			in->args[i] = PrescanMacroArg(in->args[i], yylvalpp);
		}
	}
#if 0
	printf("  <%s:%d>found macro %s\n", GetAtomString(atable, loc.file),
			loc.line, GetAtomString(atable, atom));
	for (i=0; i<in->mac->argc; i++) {
		printf("\targ %s = '", GetAtomString(atable, in->mac->args[i]));
		DumpTokenStream(stdout, in->args[i]);
		printf("'\n");
	}
#endif
	/*retain the input source*/
	in->base.prev = cpp->currentInput;
	sym->details.mac.busy = 1;
	RewindTokenStream(sym->details.mac.body);
	cpp->currentInput = &in->base;
	return 1;
} /* MacroExpand */

int ChkCorrectElseNesting(void)
{
	if (cpp->elsedepth[cpp->elsetracker] == 0) {
		cpp->elsedepth[cpp->elsetracker] = 1;
		return 1;
	}
	return 0;
}

